﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ERF.Physics
{
    /// <summary>
    /// Represents a physical body.
    /// </summary>
    public class Body
    {
        private GameObject owner;
        public float maxSpeed; // The maximum speed that this body can attain. In meters per second.
        public float maxStepSize; // The maximum height that this body can climb, per meter. In meter.
        public bool isOnGround = false;
        internal bool stateChanged;
        public Vector3 velocity;
        public Vector3 position;
        public Vector3 force;
        public Vector3 internalForce;
        public BoundingBox boundingBox;
        public float friction;
        public float restitution;
        public float mass;
        public bool isStatic;
        public event EventHandler BodyChanged;
        public event EventHandler<ERF.Physics.PhysicsEngine.BodyCollidedEventArgs> BodyCollided;

        VertexPositionColor[] vertices;
        short[] indices;

        /// <summary>
        /// Creates a new body.
        /// </summary>
        /// <param name="position">The initial position of the body.</param>
        /// <param name="box">The bounding box of the body.</param>
        /// <param name="friction">A value ranging from 0 to 1 that defines the friction of this bodys surface. (NOT IMPLEMENTED)</param>
        /// <param name="restitution">A value ranging from 0 to 1 that defines the restitution of this body. (NOT IMPLEMENTED)</param>
        /// <param name="isStatic">Defines wether the body is a static body or not. A static body is not affected by external forces.</param>
        /// <param name="mass">The mass of this body in kilograms.</param>
        /// <param name="owner">The GameObject that owns this body.</param>
        public Body(Vector3 position, BoundingBox box, float friction, float restitution, bool isStatic, float mass, GameObject owner)
        {
            this.maxSpeed = 1.33f;
            this.maxStepSize = 0.4f;
            this.position = position;
            this.boundingBox = box;
            this.friction = friction;
            this.restitution = restitution;
            this.isStatic = isStatic;
            this.mass = mass;
            this.owner = owner;

            this.vertices = new VertexPositionColor[]{
                new VertexPositionColor(box.Max, Color.Black),
                new VertexPositionColor(new Vector3(box.Max.X, box.Max.Y, box.Min.Z), Color.Black),
                new VertexPositionColor(new Vector3(box.Min.X, box.Max.Y, box.Min.Z), Color.Black),
                new VertexPositionColor(new Vector3(box.Min.X, box.Max.Y, box.Max.Z), Color.Black),
                new VertexPositionColor(box.Min, Color.Black),
                new VertexPositionColor(new Vector3(box.Min.X, box.Min.Y, box.Max.Z), Color.Black),
                new VertexPositionColor(new Vector3(box.Max.X, box.Min.Y, box.Max.Z), Color.Black),
                new VertexPositionColor(new Vector3(box.Max.X, box.Min.Y, box.Min.Z), Color.Black)
            };

            this.indices = new short[]{
                0, 1,
                1, 2,
                2, 3,
                3, 0,

                4, 5,
                5, 6,
                6, 7,
                7, 4,


                0, 6,
                1, 7,
                2, 4,
                3, 5
            };
        }

        /// <summary>
        /// Raises the BodyChanged event.
        /// </summary>
        internal void RaiseBodyChanged()
        {
            if (this.BodyChanged != null)
                this.BodyChanged(this, EventArgs.Empty);
        }

        /// <summary>
        /// Raises the BodyCollided event.
        /// </summary>
        /// <param name="other"></param>
        internal void RaiseBodyCollided(Body other)
        {
            if (this.BodyCollided != null)
                this.BodyCollided(this, new PhysicsEngine.BodyCollidedEventArgs(other));
        }

        /// <summary>
        /// Gets the owner of this body.
        /// </summary>
        public GameObject Owner
        {
            get { return this.owner; }
        }

        internal void Draw(ERFGame game, BasicEffect effect)
        {

            Camera camera = game.Camera;

            effect.World = Matrix.CreateTranslation(this.position);
            effect.View = camera.View;
            effect.Projection = camera.Projection;
            effect.VertexColorEnabled = true;
            effect.Begin();
            foreach (EffectTechnique technique in effect.Techniques)
            {
                foreach (EffectPass pass in technique.Passes)
                {
                    pass.Begin();

                    game.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
                        PrimitiveType.LineList,
                        this.vertices,
                        0,
                        this.vertices.Length,
                        this.indices,
                        0,
                        this.indices.Length / 2);

                    pass.End();
                }
            }
            effect.End();
        }
    }
}
